/*
 * Hyuga: A Density-Grid Stream Clustering Platform.
 * Copyright (C) 2014 PUC-Rio/Laboratory for Advanced Collaboration
 *
 * Hyuga is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Hyuga is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Hyuga.  If not, see <http://www.gnu.org/licenses/>.
 */
package br.pucrio.inf.lac.konoha.hyuga.common;

import br.pucrio.inf.lac.konoha.hyuga.events.LocationUpdateEvent;

/**
 * Class that wraps a set of GIS functions.
 * 
 * @author Marcos Roriz <mroriz@inf.puc-rio.br> *
 */
public class Geo {
	private static double radius = 6378137;	// earth radius

    /**
     * Calculates the distance between two mobile nodes using their location updates.
     * 
     * @param referential The base mobile node used as referential.
     * @param node Other mobile node.
     * @return the distance (in meters) from the referential location update to the other one.
     */
    public static double distanceFrom(LocationUpdateEvent referential, LocationUpdateEvent node) {
        return distanceFrom(referential.getLatitude(), referential.getLongitude(),
                            node.getLatitude(), node.getLongitude());
    }
    
    /**
     * Calculates the distance between two (latitude, longitude) points.
     * 
     * @param referential The base latitude, longitude point.
     * @param node Other (latitude, longitude) point.
     * @return the distance (in meters) from the referential point to the other one.
     */
    public static double distanceFrom(double lat, double lng, double otherLat, double otherLng) {
        double earthRadius = 6371000; //meters
        double dLat = Math.toRadians(otherLat-lat);
        double dLng = Math.toRadians(otherLng-lng);
        double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                   Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(otherLat)) *
                   Math.sin(dLng/2) * Math.sin(dLng/2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        float dist = (float) (earthRadius * c);

        return dist;
    }

    /**
     * Returns a new latitude value that is offset meters from the based latitude.
     * 
     * @param lat A latitude location.
     * @param offset The desired offset (in meters) from this latitude.
     * @return A latitude position that is offset meters from the specified latitude.
     */
	public static double getOffsetLatitude(double lat, double offset) {
		double dlat = offset / radius;
		return lat + (dlat * (180 / Math.PI));
    }

    /**
     * Returns a new longitude value that is offset meters from the based longitude.
     * 
     * @param lat A latitude location.
     * @param offset The desired offset (in meters) from this latitude.
     * @return A latitude position that is offset meters from the specified latitude.
     */
	public static double getOffsetLongitude(double lat, double lng, double offset) {
		double dlng = offset / (radius * Math.cos(Math.PI * (lat / 180)));
		return lng + (dlng * (180 / Math.PI));
	}
}
